package com.example.audiodemo.audio.impl;

import com.example.audiodemo.audio.AudioCloudPlayer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @author kalong
 * @date 2023/09/21
 */
@Slf4j
@Service
public class AudioCloudPlayerImpl implements AudioCloudPlayer {

    private final BlockingQueue<AudioData> queue = new LinkedBlockingQueue<>();
    private Thread transferAndPushThread;
    private Thread writeDataFromPipeThread;
    private static volatile boolean isSpeaking = false;
    private Process process;


    @Override
    public void init() {
        isSpeaking = true;
        clear();
    }

    @Override
    public void start(List<String> deviceNoList) {
        init();

        transferAndPushThread = new Thread(this::speak);
        transferAndPushThread.setName("transferAndPushThread-%d");
        transferAndPushThread.start();
    }

    @SneakyThrows
    @Override
    public void pushStream(byte[] voice) {
        if (ArrayUtils.isEmpty(voice)) {
            log.warn("voice is empty");
            return;
        }
        if (!isSpeaking) {
            log.warn("is not speaking, stop receive data");
            return;
        }
        queue.put(AudioData.builder()
                .data(voice)
                .build());
    }

    @Override
    public void stop() {
        clear();
        isSpeaking = false;
    }


    public void speak() {
        try {
            // 创建命名管道
            File pipe = File.createTempFile("audio_test_", ".pipe");
            pipe.delete();
            Runtime.getRuntime().exec("mkfifo " + pipe.getAbsolutePath());

            // 构造命令行
            String ffmpegCommand = "ffmpeg -re -f alaw -ar 16000 -ac 1 -i " + pipe.getAbsolutePath() + " -c:a aac -b:a 128k -f flv rtmp://192.168.42.93:1935/live";

            // 启动FFmpeg进程
            log.info("执行ffmpeg命令：{}", ffmpegCommand);
            process = Runtime.getRuntime().exec(ffmpegCommand);
            log.info("执行ffmpeg命令next");

            // 开始新的线程为FFmpeg提供数据
            writeDataFromPipeThread = new Thread(() -> {
                try {
                    // 開啟管道並寫入數據
                    OutputStream outputStream = new FileOutputStream(pipe);
                    byte[] audioData;
                    while (true) {
                        // 以下是伪代码，须替换为你的音频数据
                        audioData = queue.take().getData();
                        log.info("向管道写数据length:{}", audioData.length);
                        // 将音频数据写入管道
                        outputStream.write(audioData);
                    }
                } catch (Exception ex) {
                    // 处理异常
                    log.error("write to pipe error: ", ex);
                }
            });
            writeDataFromPipeThread.setName("writeDataFromPipeThread-%d");
            writeDataFromPipeThread.start();

            // 根据需要处理FFmpeg的标准输出和错误信息
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                log.info("ffmpeg command: {}", line);
            }

            // 确保处理 FFmpeg进程退出
            log.info("ffmpeg process waitFor start");
            process.waitFor();
            log.info("ffmpeg process waitFor end");
        } catch (Exception e) {
            log.error("speak error: ", e);
        }
    }

    private void clear() {
        if (null != writeDataFromPipeThread) {
            writeDataFromPipeThread.interrupt();
            writeDataFromPipeThread = null;
        }
        if (null != transferAndPushThread) {
            transferAndPushThread.interrupt();
            transferAndPushThread = null;
        }
        if (null != process) {
            process.destroy();
            process = null;
        }
        queue.clear();
    }

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class AudioData {
        private byte[] data;
    }

}
